package com.file.parser;


import com.file.bo.JNBank;
import com.file.bo.JNBankTran;
import com.file.bo.ResponseData;
import com.file.constant.ErrorCode;
import com.file.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import technology.tabula.*;
import technology.tabula.detectors.NurminenDetectionAlgorithm;
import technology.tabula.extractors.BasicExtractionAlgorithm;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 丰巢-江南农商行
 */
@Slf4j
public class JnBankPdfParser extends BaseDecryptPdfParser {


    public ResponseData<String> parseJNBankPdfToJson(String daId, String filePath, String pdfPassword) {
        log.info("parseJNBankPdfToJson started, daId:{}", daId);
        String json = null;

        try {
            JNBank jnBank = parseJNBankPdf(filePath, pdfPassword);
            json = JsonUtils.convertObjectToJson(jnBank);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), daId, "", "", "parseJNBankPdfToJson failed", e);
            return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(),
                    ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parseJNBankPdfToJson completed, daId:{}", daId);
        return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    private JNBank parseJNBankPdf(String filePath, String pdfPassword) {
        String pdfHeaderText = parsePdfHeaderText(filePath, pdfPassword);
        JNBank jnBank = parseJNBankHeader(pdfHeaderText);
        List<List<String>> rowList = parseFileToRowList(filePath, pdfPassword);
        parseListToBO(rowList, jnBank);
        return jnBank;
    }

    private void parseListToBO(List<List<String>> rowList, JNBank jnBank) {
        List<JNBankTran> jnBankTrans = new ArrayList<>();

        for (int i = 0; i < rowList.size(); i++) {
            List<String> rows = rowList.get(i);
            if (rows.size() >= 11) {
                JNBankTran jnBankTran = new JNBankTran();
                // 交易日期
                Pattern pattern = Pattern.compile("^\\d{8}");
                Matcher matcher = pattern.matcher(rows.get(0));
                if (matcher.find()) {
                    jnBankTran.setTransactionDate(matcher.group());
                }
                // 特别情况下先处理币种为空的情况——日期、摘要和币种在一起
                if (StringUtils.isBlank(rows.get(1)) && rows.get(0).matches("^\\d{8}\\s+(.*?)\\s+(.*)")) {
                    pattern = Pattern.compile("^\\d{8}\\s+(.*?)\\s+(.*)");
                    matcher = pattern.matcher(rows.get(0));
                    if (matcher.find()) {
                        jnBankTran.setSummary(matcher.group(1));
                        jnBankTran.setCurrencyType(matcher.group(2));
                    }
                } else if (StringUtils.isBlank(rows.get(2)) && rows.get(1).matches("(.*?)\\s+(.*)")) {
                    // 摘要和币种在一起
                    pattern = Pattern.compile("(.*?)\\s+(.*)");
                    matcher = pattern.matcher(rows.get(1));
                    if (matcher.find()) {
                        jnBankTran.setSummary(matcher.group(1));
                        jnBankTran.setCurrencyType(matcher.group(2));
                    }
                } else if(rows.get(0).matches("^\\d{8}\\s+(.*)")){
                    // 只有日期和摘要在一起，币种单独
                    pattern = Pattern.compile("^\\d{8}\\s+(.*)");
                    matcher = pattern.matcher(rows.get(0));
                    if (matcher.find()) {
                        jnBankTran.setSummary(matcher.group(1));
                        jnBankTran.setCurrencyType(rows.get(1));
                    }
                }else{
                    jnBankTran.setSummary(rows.get(1));
                    jnBankTran.setCurrencyType(rows.get(2));
                }

                // 币种和交易金额连在一起
                if (StringUtils.isBlank(jnBankTran.getCurrencyType()) && rows.get(2).split("\\s").length > 1) {
                    String[] transAmounts = rows.get(2).split("\\s");
                    jnBankTran.setCurrencyType(transAmounts[0]);
                    jnBankTran.setTransAmount(transAmounts[1]);
                } else {
                    jnBankTran.setTransAmount(rows.get(3));
                }
                jnBankTran.setBalance(rows.get(4));
                jnBankTran.setCounterpartyAccount(rows.get(5));
                jnBankTran.setCounterpartyName(rows.get(6));
                jnBankTran.setTransType(rows.get(7));
                jnBankTran.setTransChannel(rows.get(8));

                // 最后两段合二为一
                pattern = Pattern.compile("(\\S+)\\s+(\\S+)");
                matcher = pattern.matcher(rows.get(10));
                if (matcher.find()) {
                    jnBankTran.setTellerCode(matcher.group(1));
                    jnBankTran.setBranch(matcher.group(2));
                } else {
                    jnBankTran.setTellerCode(rows.get(9));
                    jnBankTran.setBranch(rows.get(10));
                }

                jnBankTrans.add(jnBankTran);
            }
        }
        jnBank.setTransactions(jnBankTrans);
    }

    private List<List<String>> parseFileToRowList(String filePath, String pdfPassword) {
        //页面所有table的记录，每一个元素代表下面一条记录cellList
        List<List<String>> rowList = new ArrayList<>();

        // 1. 读取文件
        File pdf = new File(filePath);

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf, pdfPassword)) {

            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
            NurminenDetectionAlgorithm detectionAlgorithm = new NurminenDetectionAlgorithm();
            Map<Integer, List<Rectangle>> detectedTables = new HashMap<>();

            // 4. 获取每页的PageIterator
            PageIterator pages = objectExtractor.extract();

            float rTop = 0;
            // 5. 解析每页的Rectangle(table的位置)
            while (pages.hasNext()) {
                Page page = pages.next();
                List<Rectangle> tablesOnPage = detectionAlgorithm.detect(page);
                if (!tablesOnPage.isEmpty()) {
                    detectedTables.put(page.getPageNumber(), tablesOnPage);
                } else {
                    // 适配部分pdf 表格位置识别失败设置默认值
                    Rectangle rectangle = new Rectangle(99, 0, 600, 700);
                    detectedTables.put(page.getPageNumber(), Arrays.asList(rectangle));
                }
            }

            // 6.通过table位置获取表格具体内容
            BasicExtractionAlgorithm bea = new BasicExtractionAlgorithm();

            // 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
            for (Map.Entry<Integer, List<Rectangle>> entry : detectedTables.entrySet()) {
                Page page = objectExtractor.extract(entry.getKey());
                // 默认每页只有一个表格，因此获取第0个rectangle/
                Rectangle rectangle = null; //NOSONAR
                List<Rectangle> rectangleList = entry.getValue();
                for (Rectangle r : rectangleList) {
                    double rectangleHeight = r.getHeight();
                    if (Double.compare(rectangleHeight, 0) > 0) {
//                        System.out.println(r.getTop() + " " + r.getBottom() + " " + r.getLeft() + " " + r.getRight());
                        rectangle = r;
                        break;
                    }
                }
                if (rectangle == null) {
                    throw new RuntimeException("rectangle is null.");
                }
                // 重新设置top 和bottom 值，防止表格位置识别错误
                float top = findTextBottom(filePath, page.getPageNumber(), "交易日期", pdfPassword);
                if (top > 0) {
                    rectangle.setTop(top);
                }
                rectangle.setRight(rectangle.getRight() + 300);
                float bottom = findTextBottom(filePath, page.getPageNumber(), "打印日期:", pdfPassword);
                rectangle.setBottom(bottom - 10);
                Page area = page.getArea(rectangle);
                List<Table> tableList = null;
                try {
                    tableList = bea.extract(area);
                } catch (Exception e) {
                    log.info("rectangle extract table fail.");
                    continue;
                }

                // 如果每页有多个表格，解析每一个table
                for (Table table : tableList) {
                    for (int i = 0; i < table.getRowCount(); i++) {
                        List<String> cellList = new ArrayList<>();//一条记录，每个单元格占一个元素
                        for (int j = 0; j < table.getColCount(); j++) {
                            cellList.add(table.getCell(i, j).getText(false));
                        }
                        if (cellList.get(0).isEmpty()) {
                            continue;
                        }

                        rowList.add(cellList);
                    }
                }
            }
            return rowList;
        } catch (
                Exception e) {
            throw new RuntimeException(e);
        }
    }

    private JNBank parseJNBankHeader(String pdfHeaderText) {
        JNBank jnBank = new JNBank();

        String accountId = "";
        String accountName = "";
        String startDate = "";
        String endDate = "";

        // 账号
        Pattern pattern = Pattern.compile("账号:\\s*(\\d+)");
        Matcher matcher = pattern.matcher(pdfHeaderText);
        if (matcher.find()) {
            accountId = matcher.group(1);
        }

        // 户名
        pattern = Pattern.compile("户名:\\s*([\\p{Script=Han}]+)");
        matcher = pattern.matcher(pdfHeaderText);
        if (matcher.find()) {
            accountName = matcher.group(1);
            if (StringUtils.equals(accountName, "起始日期")) {
                accountName = "";
            }
        }

        // 日期
        pattern = Pattern.compile("起始日期:\\s*(\\d{4}-\\d{2}-\\d{2})");
        matcher = pattern.matcher(pdfHeaderText);
        if (matcher.find()) {
            startDate = matcher.group(1);
        }
        pattern = Pattern.compile("结束日期:\\s*(\\d{4}-\\d{2}-\\d{2})");
        matcher = pattern.matcher(pdfHeaderText);
        if (matcher.find()) {
            endDate = matcher.group(1);
        }

        jnBank.setAccountName(accountName);
        jnBank.setAccountId(accountId);
        jnBank.setStartDate(startDate);
        jnBank.setEndDate(endDate);
        return jnBank;
    }

    static float findTextBottom(String pdfPath, int pageIndex, String searchText, String pwd) throws IOException {
        float[] y = new float[1];
        try (PDDocument document = PDDocument.load(new File(pdfPath), pwd)) {
            PDFTextStripper stripper = new PDFTextStripper() {
                @Override
                protected void writeString(String text, List<TextPosition> textPositions) {
                    StringBuilder sb = new StringBuilder();
                    for (TextPosition tp : textPositions) {
                        sb.append(tp.getUnicode());
                    }
                    if (sb.toString().contains(searchText)) {
                        TextPosition tp = textPositions.get(0);
                        y[0] = tp.getY();
                    }
                }
            };
            stripper.setSortByPosition(true);
            stripper.setStartPage(pageIndex);
            stripper.setEndPage(pageIndex);
            stripper.getText(document);
        }
        return y[0];
    }

    public static void main(String[] args) throws IOException {
        JnBankPdfParser jnBankPdfParser = new JnBankPdfParser();
        String json = "";
//        json = jnBankPdfParser.parseJNBankPdfToJson("", "D:\\data\\file\\jnbank\\江南银行客户明细对账单.pdf", "014239").getData();
//        System.out.println(json);


//        json = jnBankPdfParser.parseJNBankPdfToJson("", "D:\\data\\file\\jnbank\\江南银行客户明细对账单 (2)-242142.pdf", "242142").getData();
//        System.out.println(json);

        json = jnBankPdfParser.parseJNBankPdfToJson("", "D:\\data\\file\\jnbank\\江南银行客户明细对账单 (1)-szf（密码：27362X）.pdf", "27362X").getData();
        System.out.println(json);

//        json = jnBankPdfParser.parseJNBankPdfToJson("", "D:\\data\\file\\jnbank\\江南银行客户明细对账单_（密码：155859）.pdf", "155859").getData();
//        System.out.println(json);
    }
}